package com.share.demo.service.impl;

import com.share.demo.dao.CityDao;
import com.share.demo.domain.City;
import com.share.demo.service.CityService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * caifan 2017/8/20
 */
@Slf4j
@Service
public class CityServiceImpl implements CityService {

    @Autowired
    private CityDao cityDao;

//    @Autowired
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 获取城市逻辑：
     * 如果缓存存在，从缓存中获取城市信息
     * 如果缓存不存在，从 DB 中获取城市信息，然后插入缓存
     */
    public City findCityById(Long id) {
        // 从缓存中获取城市信息
        String key = "city_" + id;
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();


        // 缓存存在
        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            City city = (City)operations.get(key);
            log.info("CityServiceImpl.findCityById() : 从缓存中获取了城市 >> " + city.toString());
            return city;
        }

        BoundZSetOperations<String, Object> citySet = redisTemplate.boundZSetOps("city");
        Set<Object> objects = citySet.rangeByScore(0, System.currentTimeMillis());
        Iterator<Object> iterator = objects.iterator();
        List<City> cityList = new ArrayList<>();
        while (iterator.hasNext()) {
            cityList.add((City) iterator.next());
        }

        if (!cityList.isEmpty()) {
            City cityTemp = cityList.stream().filter(cityOld -> cityOld.getId().equals(id)).collect(Collectors.toList()).get(0);
            System.out.println(cityTemp.toString());
        }

        // 从 DB 中获取城市信息
        City city = cityDao.findById(id);

        // 插入缓存
        operations.set(key, city, 1000, TimeUnit.SECONDS);
        log.info("CityServiceImpl.findCityById() : 城市插入缓存 >> " + city.toString());

        return city;
    }

    @Override
    public Long saveCity(City city) {
        if (city != null) {
            BoundZSetOperations<String, Object> cityKey = redisTemplate.boundZSetOps("city");
            cityKey.add(city, System.currentTimeMillis());
        }

        return cityDao.saveCity(city);
    }

    /**
     * 更新城市逻辑：
     * 如果缓存存在，删除
     * 如果缓存不存在，不操作
     */
    @Override
    public Long updateCity(City city) {
        Long ret = cityDao.updateCity(city);

        // 缓存存在，删除缓存
        String key = "city_" + city.getId();
        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            redisTemplate.delete(key);

            log.info("CityServiceImpl.updateCity() : 从缓存中删除城市 >> " + city.toString());
        }

        BoundZSetOperations<String, Object> citySet = redisTemplate.boundZSetOps("city");
        Set<Object> objects = citySet.rangeByScore(0, System.currentTimeMillis());
        Iterator<Object> iterator = objects.iterator();
        List<City> cityList = new ArrayList<>();
        while (iterator.hasNext()) {
            cityList.add((City) iterator.next());
        }

        if (!cityList.isEmpty()) {
            City cityTemp = cityList.stream().filter(cityOld -> cityOld.getId().equals(city.getId())).collect(Collectors.toList()).get(0);
            citySet.remove(cityTemp);
            citySet.add(city, System.currentTimeMillis());
        }

        return ret;
    }

    @Override
    public Long deleteCity(Long id) {

        Long ret = cityDao.deleteCity(id);

        // 缓存存在，删除缓存
        String key = "city_" + id;
        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            redisTemplate.delete(key);

            log.info("CityServiceImpl.deleteCity() : 从缓存中删除城市 ID >> " + id);
        }
        return ret;
    }
}
